# -*- coding: UTF-8 -*-

from enum import Enum
import os
import numpy as np
from itertools import islice  
from apscheduler.schedulers.background import BackgroundScheduler
import datetime
from  common import *


class AlgoStatus(Enum):

    Creating = 1       #待启动(未结)
    Working = 2        #运行中(未结)
    Stopping = 3       #停止中(未结)
    Terminated = 5     #已停止(终态)
    Completed = 6      #完成(终态)
    Expired =7         #过期(终态)   目标数量未完成且到期时置为过期状态
    Error   =8         #错误(终态)   停牌或者股份资金不足导致持续拒单置错误状态

    @staticmethod
    def isFinalStatus(algoStatus):
        assert type(algoStatus) == AlgoStatus
        return algoStatus==AlgoStatus.Terminated or algoStatus == AlgoStatus.Completed or algoStatus == AlgoStatus.Expired or algoStatus == AlgoStatus.Error

class AlgoOrder(object):

    def __int__(self):

        self.updTime    = None
        self.custAlgoNo = None
        self.acctType   = None
        self.acct       = None
        self.symbol     = None
        self.tradeSide  = None
        self.targetVol  = None
        self.targetAmt  = None
        self.algoId     = None
        self.algoParam = {}

    def parseAlgoOrder(self,params:dict):

        key_params=['custAlgoNo','acctType','acct','symbol','tradeSide','targetVol','targetAmt','algoId']

        self.custAlgoNo = params['custAlgoNo']
        self.acctType   = params['acctType']
        self.acct       = params['acct']
        self.symbol     = params['symbol']
        self.tradeSide  = params['tradeSide']
        self.targetVol  = int(params['targetVol'])
        self.targetAmt  = float(params.get('targetAmt',0))  
        self.algoId     = params['algoId']
        self.algoParam  = { k:v for k,v in params.items() if k not in key_params if k!='updTime'}
        self.updTime    = params['updTime']

        return self

    def parseAlgoOrderStrLine(self,csvLine:str):
# updTime,custAlgoNo,acctType,acct,symbol,tradeSide,targetVol,targetAmt,algoId,algoParam
# 122850.266241,122852602,UF0,880100001155,002336.SZ,TradeSide.BUY,3000,0.0,OQ-TWAP,limitPrice=0;minAmount=0;participateRate=0.1;beginTime=122850;endTime=130459
        updTime,custAlgoNo,acctType,acct,symbol,tradeSide,targetVol,targetAmt,algoId,algoParam =csvLine.split(",")

        self.custAlgoNo = int(custAlgoNo)
        self.acctType   = acctType
        self.acct       = acct
        self.symbol     = symbol
        self.tradeSide  = tradeSide
        self.targetVol  = int(targetVol)
        self.targetAmt  = float(targetAmt)  
        self.algoId     = algoId
        self.algoParam  = { params.split("=")[0]:params.split("=")[1]  for params in algoParam.split(";")}
        self.updTime    = updTime

        return self

    def startAlgoCSVFormat(self):

        algoParam_str=';'.join([ str(k)+"="+str(v) for k,v in self.algoParam.items()])

        order_elements=[self.updTime
                ,self.custAlgoNo
                ,self.acctType
                ,self.acct
                ,self.symbol
                ,self.tradeSide
                ,self.targetVol
                ,self.targetAmt
                ,self.algoId
                ,algoParam_str
                ]
        start_str = ",".join([str(ele) for ele in order_elements])

        return start_str

    def __str__(self):
        return str(self.__dict__)
            
    # def stopAlgoCSVFormat(self):
    #     updTime=datetime.datetime.now().strftime("%Y-%m-%d %H%M%S.%f")[11:-3]   # hhmmss.sss
    #     stop_str="%s,%s,%s,%s" % (updTime,self.custAlgoNo,self.acctType,self.acct)
    #     return stop_str

class OrderRec(object):

    def __init__(self):

        self.updTime    = None
        self.orderDate = None
        self.orderTime = None 
        self.acctType   = None
        self.acct       = None
        self.symbol     = None
        self.tradeSide  = None
        self.status     = None
        self.orderQty   = None
        self.orderPrice = None  
        self.orderType  = None
        self.filledQty  = None
        self.avgPrice   = None
        self.filledAmt  = None
        self.cancelQty  = None
        self.orderNo    = None
        self.corrId     = None
        self.text        = None
        self.cliOrderId = None

    def parsOrderRec(self,csv_orderRec_str):
        order_elements = csv_orderRec_str.split(",")
        if len(order_elements)>=18:
            self.updTime    = float(order_elements[0])
            self.orderDate = order_elements[1]
            self.orderTime = order_elements[2] 
            self.acctType   = order_elements[3]
            self.acct       = order_elements[4]
            self.symbol     = order_elements[5]
            self.tradeSide  = order_elements[6]
            self.status     = int(order_elements[7])
            self.orderQty   = int(order_elements[8])
            self.orderPrice = order_elements[9] 
            self.orderType  = order_elements[10]
            self.filledQty  = int(order_elements[11])
            self.avgPrice   = float(order_elements[12])
            self.filledAmt  = float(order_elements[13])
            self.cancelQty  = int(order_elements[14])
            self.orderNo    = order_elements[15]
            self.corrId     = order_elements[16]
            self.text        = order_elements[17]
            self.cliOrderId    = order_elements[18]
        else:
            printLog("订单回报字段长度异常:%d,%s" + (len(order_elements) ,csv_orderRec_str))

    def __str__(self):
        return str(self.__dict__)

class AlgoExecStatic(object):
    
    def __init__(self):

        self.updTime=None
        self.acctType=None
        self.acct=None
        self.algoInstanceId=None
        self.custAlgoNo=None
        self.status=None
        self.symbol=None
        self.tradeSide=None
        self.commitTime=None
        self.beginTime=None
        self.endTime=None
        self.progress=None
        self.targetVol=None
        self.filledQty=None
        self.filledAmt=None
        self.avgPrice=None
        self.cancelOrderRate=None
        self.errorCode=None
        self.errorMsg=None

    def parseAlgoExecStatic(self,csv_exec_result_str):
        field_elements = csv_exec_result_str.split(",")
        if len(field_elements)>=6:
            self.updTime    =field_elements[0]
            self.acctType   =field_elements[1]
            self.acct          =field_elements[2]
            self.algoInstanceId=field_elements[3]
            self.custAlgoNo     = int(field_elements[4])
            self.status         = int(field_elements[5])
            self.symbol         = field_elements[6]
            self.tradeSide      = field_elements[7]
            self.commitTime     = field_elements[8]
            self.beginTime      = field_elements[9]
            self.endTime        = field_elements[10]
            self.progress       = get_float(field_elements[11])
            self.targetVol     = get_int(field_elements[12])
            self.filledQty      = get_int(field_elements[13])
            self.filledAmt      = get_float(field_elements[14])
            self.avgPrice           = get_float(field_elements[15])
            self.cancelOrderRate    = get_float(field_elements[16])
            self.errorCode      = field_elements[17]
            self.errorMsg       = field_elements[18]

    def __str__(self):
        return str(self.__dict__)

    def stopAlgoCSVFormat(self):
        updTime=datetime.datetime.now().strftime("%Y-%m-%d %H%M%S.%f")[11:-3]   # hhmmss.sss
        stop_str="%s,%s,%s,%s" % (updTime,self.custAlgoNo,self.acctType,self.acct)
        return stop_str

class OrderBook(object):
        
    def __init__(self,algo_dir,date):

        self.algo_dir=algo_dir
        self.algoStart_path=os.path.join(self.algo_dir,'algoStart_' + date + '.csv')
        self.orderUpdate_path=os.path.join(self.algo_dir,'orderUpdate_' + date + '.csv')
        self.execResult_path=os.path.join(self.algo_dir,'algoExecStatic_' + date + '.csv')

        #print(self.orderUpdate_path,self.exec_result_path )
        
        self.isRunning=False
        self.custAlgoNo2AlgoOrders={}
        self.custAlgoNo2AlgoExecStatic={}
        self.custAlgoNo2AlgoExecStatic_cancel={}
        self.custAlgoNo2AlgoExecStatic_running={}
        self.orderRecs={}
        self.positions={}
                
        self.orderRecs_last_location=0
        self.execResults_last_location=0
        
        self.symbol2QtySummary={}
        

    def init_history(self):
        # 读取之前已发送的算法母单
        if os.path.exists(self.algoStart_path):
            with open(self.algoStart_path,'r') as f:
                for line in islice(f,1,None):       # 跳过首行字段
                    algoOrder = AlgoOrder()
                    algoOrder.parseAlgoOrderStrLine(line.strip())
                    self.custAlgoNo2AlgoOrders.update({algoOrder.custAlgoNo:algoOrder})
                    #print(custAlgoNo2AlgoOrders)

        if os.path.exists(self.orderUpdate_path):
            with open(self.orderUpdate_path,'r') as f:
                for line in islice(f,1,None):       # 跳过首行字段
                    orderRec = OrderRec()
                    orderRec.parsOrderRec(line.strip())
                    self.orderRecs.update({orderRec.cliOrderId:orderRec})
                    #print(orderRec)
                self.orderRecs_last_location=f.tell()

        if os.path.exists(self.execResult_path):
            with open(self.execResult_path,'r') as f:
                for line in islice(f,1,None):       # 跳过首行字段
                    execr = AlgoExecStatic()
                    execr.parseAlgoExecStatic(line.strip())
                    self.custAlgoNo2AlgoExecStatic.update({execr.custAlgoNo:execr})
                    if not AlgoStatus.isFinalStatus(AlgoStatus(execr.status)):
                        self.custAlgoNo2AlgoExecStatic_running.update({execr.custAlgoNo:execr})
                    else:
                        if execr.custAlgoNo in self.custAlgoNo2AlgoExecStatic_running:
                            self.custAlgoNo2AlgoExecStatic_running.pop(execr.custAlgoNo)
                    #print(execr)
                self.execResults_last_location=f.tell()

        printLog("初始化订单簿,母单数量:%d,运行数量:%d,子单数:%d" % (len(self.custAlgoNo2AlgoExecStatic),len(self.custAlgoNo2AlgoExecStatic_running),len(self.orderRecs)))
    
        
    def getOrderRecs(self,cliOrderId):
        return self.orderRecs.get(cliOrderId,None)

    def addDMAOrder(self,dmaOrder):
        self.dma_orders[dmaOrder.cliOrderId]=dmaOrder

    def read_orderUpdates(self):
        if os.path.exists(self.orderUpdate_path):
            with open(self.orderUpdate_path,'r') as f:
                f.seek(self.orderRecs_last_location)
                beginline=0
                if self.orderRecs_last_location==0:     # 跳过首行字段,如init_history执行时,orderUpdate_path文件尚不存在，这里要跳过首行字段
                    beginline=1
                lines=islice(f,beginline,None)
                if self.isRunning:
                    for line in lines:     #读增量内容
                        if len(line.strip())>=2:
                            orderRec = OrderRec()
                            orderRec.parsOrderRec(line.strip())
                            self.orderRecs.update({orderRec.cliOrderId:orderRec})
                    cur_location = f.tell()        
                    self.orderRecs_last_location = cur_location

    def read_execResults(self):
        if os.path.exists(self.execResult_path):
            with open(self.execResult_path,'r') as f:
                f.seek(self.execResults_last_location)
                beginline=0
                if self.execResults_last_location==0:     # 跳过首行字段
                    beginline=1
                lines=islice(f,beginline,None)
                if self.isRunning:
                    for line in lines:     #读增量内容
                        if len(line.strip())>=2:
                            execr = AlgoExecStatic()
                            execr.parseAlgoExecStatic(line.strip())
                            self.custAlgoNo2AlgoExecStatic.update({execr.custAlgoNo:execr})
                        if not AlgoStatus.isFinalStatus(AlgoStatus(execr.status)):
                            self.custAlgoNo2AlgoExecStatic_running.update({execr.custAlgoNo:execr})
                        else:
                            if execr.custAlgoNo in self.custAlgoNo2AlgoExecStatic_running:
                                self.custAlgoNo2AlgoExecStatic_running.pop(execr.custAlgoNo)
                    cur_location = f.tell()        
                    self.execResults_last_location = cur_location
                    
    def run(self):
        # 在此实现 增量读取订单发送响应结果文件:execResult_path 
        # 实现 增量读取订单回报文件       :orderUpdate_path 
        # 实现 读取仓位信息 等等 其他回写文件等
        self.isRunning=True
        self.scheduler = BackgroundScheduler()
        self.scheduler.add_job(self.read_orderUpdates,'interval', seconds=2,max_instances=1,id='id_read_orderUpdates',coalesce =True)
        self.scheduler.add_job(self.read_execResults ,'interval', seconds=2,max_instances=1,id='id_read_execResults' ,coalesce =True)        
        self.scheduler.start()
        
    def stop(self):
        self.isRunning=False
        # self.scheduler.remove_job('id_read_orderUpdates')
        # self.scheduler.remove_job('id_read_execResults')
        self.scheduler.shutdown(wait=False)
        printLog("订单簿停止刷新,母单总数量:%d,运行数量:%d,子单数:%d" % (len(self.custAlgoNo2AlgoExecStatic),len(self.custAlgoNo2AlgoExecStatic_running),len(self.orderRecs)))
        printLog("OrderBook stop reading flies in " + self.algo_dir)